package com.shailan.projects.modelviewer.applet;

import com.sun.opengl.util.GLUT;
import demos.util.Bunny;
import demos.util.ObjReader;
import gleem.ExaminerViewer;
import java.awt.*;
import java.awt.event.*;

import javax.media.opengl.*;
import javax.media.opengl.glu.*;

import java.io.*;

/**
 * GLRenderer.java <BR>
 * author: Brian Paul (converted to Java by Ron Cemer and Sven Goethel) <P>
 *
 * This version is equal to Brian Paul's version 1.2 1999/10/21
 */
public class GLRenderer implements GLEventListener, MouseListener, MouseMotionListener, KeyListener, MouseWheelListener {

    private float view_rotx = 20.0f,  view_roty = 30.0f,  view_rotz = 0.0f;
    private float scale = 3.0f;
    private int objGlList,objGlList2;
    private float angle = 0.0f;
    private int prevMouseX,  prevMouseY;
    private boolean mouseRButtonDown = false;
    private boolean firstRender = true;
    private GLAutoDrawable drawObject;
    private GL gl;
    private GLU glu = new GLU();
    private GLUT glut = new GLUT();
    private ExaminerViewer viewer;
    public int obj=0;
    public boolean toggleWire=true;
    private ObjReader model;
    private String modelFileName="demos/data/models/teapot.obj";
    private String modelFileName2="demos/data/models/bunny.txt";

    public void init(GLAutoDrawable drawable) {
        // Use debug pipeline
        // drawable.setGL(new DebugGL(drawable.getGL()));
        this.setDrawObject(drawable);
        this.setGl(drawable.getGL());
        GL gl = drawable.getGL();

        System.err.println("INIT GL IS: " + gl.getClass().getName());

        System.err.println("Chosen GLCapabilities: " + drawable.getChosenGLCapabilities());

        gl.setSwapInterval(1);


        float pos[] = {5.0f, 5.0f, 10.0f, 0.0f};
        float red[] = {0.8f, 0.1f, 0.0f, 1.0f};
        float green[] = {0.0f, 0.8f, 0.2f, 1.0f};
        float blue[] = {0.2f, 0.2f, 1.0f, 1.0f};

        gl.glLightfv(GL.GL_LIGHT0, GL.GL_POSITION, pos, 0);
        gl.glEnable(GL.GL_CULL_FACE);
        gl.glEnable(GL.GL_LIGHTING);
        gl.glEnable(GL.GL_LIGHT0);
        gl.glEnable(GL.GL_DEPTH_TEST);

        
        loadModel();

        gl.glEnable(GL.GL_NORMALIZE);

        drawable.addMouseListener(this);
        drawable.addMouseMotionListener(this);
        drawable.addKeyListener(this);
        drawable.addMouseWheelListener(this);
    }

    public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) {
        GL gl = drawable.getGL();

        float h = (float) height / (float) width;

        gl.glMatrixMode(GL.GL_PROJECTION);

        System.err.println("GL_VENDOR: " + gl.glGetString(GL.GL_VENDOR));
        System.err.println("GL_RENDERER: " + gl.glGetString(GL.GL_RENDERER));
        System.err.println("GL_VERSION: " + gl.glGetString(GL.GL_VERSION));
        gl.glLoadIdentity();
        gl.glFrustum(-1.0f, 1.0f, -h, h, 5.0f, 60.0f);
        gl.glMatrixMode(GL.GL_MODELVIEW);
        gl.glLoadIdentity();
        gl.glTranslatef(0.0f, 0.0f, -40.0f);
    }

    public void display(GLAutoDrawable drawable) {
        // Get the GL corresponding to the drawable we are animating
        GL gl = drawable.getGL();

        // Special handling for the case where the GLJPanel is translucent
        // and wants to be composited with other Java 2D content
        if ((drawable instanceof GLJPanel) &&
                !((GLJPanel) drawable).isOpaque() &&
                ((GLJPanel) drawable).shouldPreserveColorBufferIfTranslucent()) {
            gl.glClear(GL.GL_DEPTH_BUFFER_BIT);
        } else {
            gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
        }

        // Rotate the entire assembly of gears based on how the user
        // dragged the mouse around
        gl.glPushMatrix();
        gl.glRotatef(view_rotx, 1.0f, 0.0f, 0.0f);
        gl.glRotatef(view_roty, 0.0f, 1.0f, 0.0f);
        gl.glRotatef(view_rotz, 0.0f, 0.0f, 1.0f);
        gl.glScalef(scale, scale, scale);
        
        if(toggleWire){
            gl.glPolygonMode(GL.GL_FRONT_AND_BACK, GL.GL_LINE);
        }    

        this.drawObj(gl, obj);
        // Remember that every push needs a pop; this one is paired with
        // rotating the entire gear assembly
        gl.glPopMatrix();


    }

    public void displayChanged(GLAutoDrawable drawable, boolean modeChanged, boolean deviceChanged) { }

    public void drawObj(GL gl, int obj) {
        switch (obj) {
            case 0:
                gl.glCallList(objGlList2);
                break;
            case 1:
                gl.glCallList(objGlList);
                break;
            case 2:
                glut.glutSolidSphere(0.5, 64, 64);
                break;
            case 3:
                glut.glutSolidTorus(0.25, 0.5, 64, 64);
                break;

            default:
                gl.glCallList(objGlList2);
                break;
        }
    }


    // Methods required for the implementation of MouseListener
    public void mouseEntered(MouseEvent e) {
    }

    public void mouseExited(MouseEvent e) {
    }

    public void mouseWheelMoved(MouseWheelEvent e) {
        int notches = e.getWheelRotation();
        if (scale - notches * 0.2f > 0) {
            //Mouse Wheel Moved UP
            scale = scale - notches * 0.2f;
        }

    }

    public void mousePressed(MouseEvent e) {
        prevMouseX = e.getX();
        prevMouseY = e.getY();
        if ((e.getModifiers() & e.BUTTON3_MASK) != 0) {
            mouseRButtonDown = true;
            
        }
    }

    public void mouseReleased(MouseEvent e) {
        if ((e.getModifiers() & e.BUTTON3_MASK) != 0) {
            mouseRButtonDown = false;
        }
    }

    public void mouseClicked(MouseEvent e) {
    }

    // Methods required for the implementation of MouseMotionListener
    public void mouseDragged(MouseEvent e) {
        int x = e.getX();
        int y = e.getY();
        Dimension size = e.getComponent().getSize();

        float thetaY = 360.0f * ((float) (x - prevMouseX) / (float) size.width);
        float thetaX = 360.0f * ((float) (prevMouseY - y) / (float) size.height);

        prevMouseX = x;
        prevMouseY = y;

        view_rotx += thetaX * 0.1;
        view_roty += thetaY * 0.1;

    }

    public void mouseMoved(MouseEvent e) {
    }
    private boolean[] b = new boolean[256];

    private void dispatchKey(char k) {
        setFlag(k, !getFlag(k));

        System.out.println("Pressed key:" + k);

        switch (k) {
            case '0':
                obj=0;
                break;

            case '1':
                obj = 1;
                break;

            case '2':
                obj = 2;
                break;
            case '3':
                obj = 3;
                break;

            case 'd':
               view_rotz -= 5;
                break;

            case 's':
                //doViewAll = true;
                scale = scale - 1;
                break;
            case 'w':
                //toggleWire = true;
                scale++;
                break;
            case 'a':
                view_rotz += 5;
                break;
             case 'q':
                toggleWire = !toggleWire;
                break;    
            default:
                break;
        }
    }

    private void setFlag(char key, boolean val) {
        b[((int) key) & 0xFF] = val;
    }

    private boolean getFlag(char key) {
        return b[((int) key) & 0xFF];
    }

    /**
     * @return the drawObject
     */
    public GLAutoDrawable getDrawObject() {
        return drawObject;
    }

    /**
     * @param drawObject the drawObject to set
     */
    public void setDrawObject(GLAutoDrawable drawObject) {
        this.drawObject = drawObject;
    }

    public void keyReleased(KeyEvent e) {
    }

    public void keyPressed(KeyEvent e) {
    }

    public void keyTyped(KeyEvent e) {
        dispatchKey(e.getKeyChar());
    }
    
    public void loadObj(){
        
        try {
        InputStream in = getClass().getClassLoader().getResourceAsStream(this.getModelFileName());
        if (in == null) {
          throw new IOException("Unable to open model file " + this.getModelFileName());
        }
        model = new ObjReader(in);
        if (model.getVerticesPerFace() != 3) {
          throw new IOException("Sorry, only triangle-based WaveFront OBJ files supported");
        }
        model.rescale(1.2f / model.getRadius());
        
      } catch (IOException e) {
        e.printStackTrace();
        System.exit(1);
      }
      objGlList=model.gen3DObjectList(this.gl);
      
    }
    
    public void loadModel(){
        
        loadObj();
        try{
            objGlList2=Bunny.gen3DObjectList(gl);
        }catch(Exception ex){
            ex.printStackTrace();
        }
        
    
    }

    /**
     * @return the modelFileName
     */
    public String getModelFileName() {
        return modelFileName;
    }

    /**
     * @param modelFileName the modelFileName to set
     */
    public void setModelFileName(String modelFileName) {
        this.modelFileName = modelFileName;
    }

    /**
     * @return the gl
     */
    public GL getGl() {
        return gl;
    }

    /**
     * @param gl the gl to set
     */
    public void setGl(GL gl) {
        this.gl = gl;
    }
}

